package v2.chapter9.jaas2;

import java.security.Principal;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class MyLoginModule implements LoginModule {
	
	private String username;
	private char[] password;
	
	private boolean userPwdSucceeded=false;
	private boolean commitSucceeded =false;
	
	private Principal userPrincipal;
	
	private Subject subject;
	private CallbackHandler callbackHandler;

	@Override
	public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
			Map<String, ?> options) { 
		this.subject=subject;
		this.callbackHandler=callbackHandler;
	}

	@Override
	public boolean login() throws LoginException {

		if(callbackHandler==null) 
			throw new LoginException("Error: no CallbackHandler available to garner authentication information from the user");
		
		Callback[] callbacks=new Callback[4];
		callbacks[0]=new NameCallback("user name");
		callbacks[1]=new PasswordCallback("password",false);
		callbacks[2] = new TextOutputCallback(TextOutputCallback.INFORMATION, "hello, just a msg!");
	    callbacks[3] = new TextOutputCallback(TextOutputCallback.WARNING, "just warn you!");
 
	    try {
	    	
	    	callbackHandler.handle(callbacks);
	    	
	    	NameCallback nameCallback=(NameCallback)callbacks[0];
	    	PasswordCallback passwordCallback=(PasswordCallback)callbacks[1];
	    	
	    	username=nameCallback.getName();
	    	char[] temPassword=passwordCallback.getPassword();
	    	
	    	if(temPassword==null) {
	    		temPassword=new char[0];
	    	}
	    	
	    	password=new char[temPassword.length];
	    	System.arraycopy(temPassword, 0, password,0, temPassword.length); 
	    }catch(Exception e){
	    	e.printStackTrace();
	    }

	    boolean usernameCorrect = false;
        if (username.equals("user")) usernameCorrect = true;
 
        if (usernameCorrect &&
                password.length == 3 &&
                password[0] == 'p' &&
                password[1] == 'w' &&
                password[2] == 'd') {
 
            userPwdSucceeded = true;
        } else {
            userPwdSucceeded = false;
            cleanUserAndPwdData();
            if (!usernameCorrect) {
                throw new FailedLoginException("User Name Incorrect");
            } else {
                throw new FailedLoginException("Password Incorrect");
            }
        }
        return userPwdSucceeded; 
	}

	@Override
	public boolean commit() throws LoginException {
		if(!userPwdSucceeded) return false;
		
		userPrincipal=new SamplePrincipal(username);
		subject.getPrincipals().add(userPrincipal);
		
		cleanUserAndPwdData();
		return commitSucceeded=true;
	}

	@Override
	public boolean abort() throws LoginException {
		if(!userPwdSucceeded) return false;
		
		if(commitSucceeded) logout();
		else cleanState();
		return true;
	}

	@Override
	public boolean logout() throws LoginException {
		subject.getPrincipals().remove(userPrincipal);
		cleanState();
		userPwdSucceeded=commitSucceeded;
		return false;
	}
	
	private void cleanState() {
		userPwdSucceeded=false;
		cleanUserAndPwdData();
		userPrincipal=null;
	}

	private void cleanUserAndPwdData() {
		username=null;
		if(password!=null) {
			for(int i=0;i<password.length;i++) {
				password[i]=' ';
			}
			password=null;
		}
	}
}
